/**************************************************************************** **
 * Copyright (C) 2001-2020 Inhand Networks, Inc.
 **************************************************************************** **/

/* ************************************************************************** **
 *     MODULE NAME            : system
 *     LANGUAGE               : C
 *     TARGET ENVIRONMENT     : Any
 *     FILE NAME              : mqtt_client.c
 *     FIRST CREATION DATE    : 2022/01/06
 * --------------------------------------------------------------------------
 *     Version                : 1.0
 *     Author                 : EExuke
 *     Last Change            : 2022/01/06
 *     FILE DESCRIPTION       : ref: mosquitto/examples
** ************************************************************************** */

#include "/home/xuke/bin/dbg.h"
#include "mqtt_client.h"
#include "ctiot_topics.h"

static void set_params_handle(const struct mosquitto_message *msg);
static void get_params_handle(const struct mosquitto_message *msg);
static void esim_act_handle(const struct mosquitto_message *msg);
static void factory_rest_handle(const struct mosquitto_message *msg);
static void once_report_handle(const struct mosquitto_message *msg);
static void set_networking_handle(const struct mosquitto_message *msg);
static void reboot_handle(const struct mosquitto_message *msg);
static void get_position_handle(const struct mosquitto_message *msg);
static void sim_switch_handle(const struct mosquitto_message *msg);
static void set_net_mode_handle(const struct mosquitto_message *msg);
static void set_wan_mode_handle(const struct mosquitto_message *msg);
static void onoff_wifi_handle(const struct mosquitto_message *msg);
static void set_wifi_handle(const struct mosquitto_message *msg);
static void onoff_dnn_handle(const struct mosquitto_message *msg);
static void set_dnn_handle(const struct mosquitto_message *msg);
static void del_dnn_handle(const struct mosquitto_message *msg);
static void onoff_ipv6_handle(const struct mosquitto_message *msg);
static void onoff_flow_limit_handle(const struct mosquitto_message *msg);
static void flow_threshold_handle(const struct mosquitto_message *msg);
static void ping_handle(const struct mosquitto_message *msg);
static void ping_result_handle(const struct mosquitto_message *msg);
static void upgrade_handle(const struct mosquitto_message *msg);
static void onoff_sun_net_handle(const struct mosquitto_message *msg);

static SUB_TOPIC_NODE gl_ctiot_sub_topic_list[] = {
	{ .topic = CTIOT_TOPIC_SET_PARAMS,       .callback = set_params_handle },
	{ .topic = CTIOT_TOPIC_GET_PARAMS,       .callback = get_params_handle },
	{ .topic = CTIOT_TOPIC_ESIM_ACT,         .callback = esim_act_handle },
	{ .topic = CTIOT_TOPIC_FACTORY_RST,      .callback = factory_rest_handle },
	{ .topic = CTIOT_TOPIC_ONCE_REPORT,      .callback = once_report_handle },
	{ .topic = CTIOT_TOPIC_SET_NET,          .callback = set_networking_handle},
	{ .topic = CTIOT_TOPIC_REBOOT,           .callback = reboot_handle },
	{ .topic = CTIOT_TOPIC_GET_POSITION,     .callback = get_position_handle },
	{ .topic = CTIOT_TOPIC_SIM_SWITCH,       .callback = sim_switch_handle },
	{ .topic = CTIOT_TOPIC_SET_NET_MODE,     .callback = set_net_mode_handle },
	{ .topic = CTIOT_TOPIC_SET_WAN_MODE,     .callback = set_wan_mode_handle },
	{ .topic = CTIOT_TOPIC_ONOFF_WIFI,       .callback = onoff_wifi_handle },
	{ .topic = CTIOT_TOPIC_SET_WIFI,         .callback = set_wifi_handle },
	{ .topic = CTIOT_TOPIC_ONOFF_DNN,        .callback = onoff_dnn_handle },
	{ .topic = CTIOT_TOPIC_SET_DNN,          .callback = set_dnn_handle },
	{ .topic = CTIOT_TOPIC_DEL_DNN,          .callback = del_dnn_handle },
	{ .topic = CTIOT_TOPIC_ONOFF_IPV6,       .callback = onoff_ipv6_handle },
	{ .topic = CTIOT_TOPIC_ONOFF_FLOW_LIMIT, .callback = onoff_flow_limit_handle },
	{ .topic = CTIOT_TOPIC_FLOW_THRESHOLD,   .callback = flow_threshold_handle },
	{ .topic = CTIOT_TOPIC_PING,             .callback = ping_handle },
	{ .topic = CTIOT_TOPIC_PING_RESULT,      .callback = ping_result_handle },
	{ .topic = CTIOT_TOPIC_UPGRADE,          .callback = upgrade_handle },
	{ .topic = CTIOT_TOPIC_ONOFF_SUB_NET,    .callback = onoff_sun_net_handle },
};

static const int gl_sub_topic_nums = (sizeof(gl_ctiot_sub_topic_list))/(sizeof(gl_ctiot_sub_topic_list[0]));

/***************************************************************************************
 * Functions
 ***************************************************************************************/
static void set_params_handle(const struct mosquitto_message *msg)
{
	int task_id;
	json_t *msg_obj = NULL;
	json_t *rsp_obj = NULL;
	json_error_t failed;

	//recv task msg and parse taskId
	msg_obj = json_loads((char*)msg->payload, JSON_INDENT(0), &failed);
	if (!msg_obj) {
		printf("failed to get msg_obj %d:%s\n", failed.line, failed.text);
	}
	task_id = json_integer_value(json_object_get(msg_obj, "taskId"));

	/* InhandTag: do something */
	my_debug_msg("do something");

	//response task by taskID
	rsp_obj = json_pack("{s:i, s:{s:i, s:s}}", "taskId", task_id, "resultPayload", "act_result", 0, "act_msg", "ok");
	if (!rsp_obj) {
		printf("failed to get rsp_obj");
	}
	general_msg_publish(CTIOT_TOPIC_REBOOT_RESP, json_dumps(rsp_obj, JSON_INDENT(0)));

	json_decref(msg_obj);
	json_decref(rsp_obj);
}

static void get_params_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_GET_PARAMS_RESP, payload);
}

static void esim_act_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ESIM_ACT_RESP, payload);
}
static void factory_rest_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_FACTORY_RST_RSP, payload);
}

static void once_report_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONCE_REPORT_RESP, payload);
}

static void set_networking_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SET_NET_RESP, payload);
}

static void reboot_handle(const struct mosquitto_message *msg)
{
	int task_id;
	json_t *msg_obj = NULL;
	json_t *rsp_obj = NULL;
	json_error_t failed;

	//recv task msg and parse taskId
	msg_obj = json_loads((char*)msg->payload, JSON_INDENT(0), &failed);
	if (!msg_obj) {
		printf("failed to get msg_obj %d:%s\n", failed.line, failed.text);
	}
	task_id = json_integer_value(json_object_get(msg_obj, "taskId"));

	/* InhandTag: do something */
	my_debug_msg("do something");

	//response task by taskID
	rsp_obj = json_pack("{s:i, s:{s:i, s:s}}", "taskId", task_id, "resultPayload", "act_result", 0, "act_msg", "ok");
	if (!rsp_obj) {
		printf("failed to get rsp_obj");
	}
	general_msg_publish(CTIOT_TOPIC_REBOOT_RESP, json_dumps(rsp_obj, JSON_INDENT(0)));

	json_decref(msg_obj);
	json_decref(rsp_obj);
}

static void get_position_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_GET_POSITION_RESP, payload);
}

static void sim_switch_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SIM_SWITCH_RESP, payload);
}

static void set_net_mode_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SET_NET_MODE_RESP, payload);
}

static void set_wan_mode_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SET_WAN_MODE_RESP, payload);
}

static void onoff_wifi_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONOFF_WIFI_RESP, payload);
}

static void set_wifi_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SET_WIFI_RESP, payload);
}

static void onoff_dnn_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONOFF_DNN_RESP, payload);
}

static void set_dnn_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_SET_DNN_RESP, payload);
}

static void del_dnn_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_DEL_DNN_RESP, payload);
}

static void onoff_ipv6_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONOFF_IPV6_RESP, payload);
}

static void onoff_flow_limit_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONOFF_FLOW_LIMIT_RESP, payload);
}

static void flow_threshold_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_FLOW_THRESHOLD_RESP, payload);
}

static void ping_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_PING_RESP, payload);
}

static void ping_result_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_PING_RESULT_RESP, payload);
}

static void upgrade_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_UPGRADE_RESP, payload);
}

static void onoff_sun_net_handle(const struct mosquitto_message *msg)
{
	printf("Topic:%s, Qos:%d Data:%s\n", msg->topic, msg->qos, (char *)msg->payload);
	char payload[] = "{\"act_result\":0,\"act_msg\":\"ok\"}";

	general_msg_publish(CTIOT_TOPIC_ONOFF_SUB_NET_RESP, payload);
}

static void on_connect(struct mosquitto *mosq, void *obj, int reason_code)
{
	int rc;
	SUB_TOPIC_NODE *p = gl_ctiot_sub_topic_list;

	printf("on_connect: %s\n", mosquitto_connack_string(reason_code));
	if(reason_code != 0){
		printf("disconnect mosquitto...\n");
		mosquitto_disconnect(mosq);
		exit(EXIT_FAILURE);
	}

	gl_mqtt_state = MQTT_STATE_CONNECTED;

	// subscrib topic list
	for (int i=0; i<gl_sub_topic_nums; i++) {
		if (p[i].topic[0] == '\0') {
			continue;
		}

		rc = mosquitto_subscribe(mosq, NULL, p[i].topic, 1);
		if(rc != MOSQ_ERR_SUCCESS){
			fprintf(stderr, "Error subscribing: %s\n", mosquitto_strerror(rc));
			mosquitto_disconnect(mosq);
			exit(EXIT_FAILURE);
		}

		printf("subscribe topic:%s SUCCESS!\n", p[i].topic);
	}
}

static void on_publish(struct mosquitto *mosq, void *obj, int mid)
{
	printf("Message with mid %d has been published.\n", mid);
}

/* Callback called when the broker sends a SUBACK in response to a SUBSCRIBE. */
static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
	int i;
	bool have_subscription = false;

	/* In this example we only subscribe to a single topic at once, but a
	 * SUBSCRIBE can contain many topics at once, so this is one way to check
	 * them all. */
	for(i=0; i<qos_count; i++){
		/*printf("on_subscribe: %d:granted qos = %d\n", i, granted_qos[i]);*/
		if(granted_qos[i] <= 2){
			have_subscription = true;
		}
	}
	if(have_subscription == false){
		/* The broker rejected all of our subscriptions, we know we only sent
		 * the one SUBSCRIBE, so there is no point remaining connected. */
		fprintf(stderr, "Error: All subscriptions rejected.\n");
		mosquitto_disconnect(mosq);
	}
}

/* Callback called when the client receives a message. */
static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
	SUB_TOPIC_NODE *p = gl_ctiot_sub_topic_list;

	//call handle
	for (int i=0; i<gl_sub_topic_nums; i++) {
		if (strcmp(p[i].topic, msg->topic)) {
			continue;
		}

		p[i].callback(msg);
		break;
	}
}


/****************************************************************************
 *  Description   : mqtt connect
 ****************************************************************************/
int mqtt_client_connect()
{
	int rc;

	/* 1. Required before calling other mosquitto functions */
	mosquitto_lib_init();

	/* 2. Create a new client instance.
	 * id = NULL -> ask the broker to generate a client id for us
	 * clean session = true -> the broker should remove old sessions when we connect
	 * obj = NULL -> we aren't passing any of our private data for callbacks
	 */
	gl_mosq = mosquitto_new(CTIOT_CLIENT_ID, true, NULL);
	if(gl_mosq == NULL){
		fprintf(stderr, "Error: Out of memory.\n");
		return 1;
	}

	/* 3. Configure callbacks. This should be done before connecting ideally. */
	mosquitto_username_pw_set(gl_mosq, "EExuke", CTIOT_PASSWORD);    //set username and passward

	mosquitto_connect_callback_set(gl_mosq, on_connect);
	mosquitto_publish_callback_set(gl_mosq, on_publish);
	mosquitto_subscribe_callback_set(gl_mosq, on_subscribe);
	mosquitto_message_callback_set(gl_mosq, on_message);

	/* 4. Connect to broker on port 1883, with a keepalive of 60 seconds.
	 * This call makes the socket connection only, it does not complete the MQTT
	 * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or mosquitto_loop_forever() for processing net traffic. */
	rc = mosquitto_connect(gl_mosq, CTIOT_ENDPOINT, CTIOT_TCP_PORT, 60);
	if(rc != MOSQ_ERR_SUCCESS){
		mosquitto_destroy(gl_mosq);
		fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
		return 1;
	}

	/* 5. Run the network loop in a background thread, this call returns quickly. */
	rc = mosquitto_loop_start(gl_mosq);
	if(rc != MOSQ_ERR_SUCCESS){
		mosquitto_destroy(gl_mosq);
		fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
		return 1;
	}

	return 0;
}

void mqtt_client_disconnect(struct mosquitto *gl_mosq)
{
	mosquitto_destroy(gl_mosq);
	mosquitto_lib_cleanup();
}

void general_msg_publish(char *topic, char *payload)
{
	int rc;

	if (gl_mosq == NULL) {
		printf("gl_mosq is null\n");
		return;
	}

	rc = mosquitto_publish(gl_mosq, NULL, topic, strlen(payload), payload, 1, false);
	if(rc != MOSQ_ERR_SUCCESS){
		fprintf(stderr, "Error publishing: %s\n", mosquitto_strerror(rc));
	}
}

